﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Downloader;
using WatiN.Core;
using Image = WatiN.Core.Image;

namespace Parser.Parsers
{
    public class OdnoklassnikiParser:BaseParser
    {

        #region Fields

        private int _curPage = -1;
        private int _curItem = -1;
        private int _pause = 100;
        private string _tmp;
        private IE _ie;
        //private FireFox _ff;
        //private Chrome _ch;
        private const string frienId = "FRIENDID=";
        public bool IsContinue { get; set; }

        #endregion

        #region Properties

        private string Server { get; set; }
        private const int _timeout = 1;
        private Browser Browser
        {
            get
            {
                if (_ie == null)
                //if (_ff == null)
                //if (_ch == null)
                {
                    Settings.WaitForCompleteTimeOut = _timeout;
                    Settings.AttachToBrowserTimeOut = _timeout;
                    Settings.WaitUntilExistsTimeOut = _timeout;
                    _ie = new IE {Visible = true, AutoClose = true,};
                    //_ff = new FireFox();// { Visible = true, AutoClose = true, };
                    //_ch = new Chrome(NetworkPath);
                    Settings.WaitForCompleteTimeOut = _timeout;
                    Settings.AttachToBrowserTimeOut = _timeout;
                    Settings.WaitUntilExistsTimeOut = _timeout;
                    Settings.AutoCloseDialogs = true;
                }
                return _ie;
                //return _ff;
                //return _ch;
            }
        }
        
        #endregion

        #region Init

        public OdnoklassnikiParser()
        {
            MainInit();
        }
        public OdnoklassnikiParser(string login, string password)
        {
            MainInit();
            Login = login;
            Password = password;
        }
        public OdnoklassnikiParser(string login, string password, string storePath)
        {
            MainInit();
            Login = login;
            Password = password;
            StorePath = storePath;
        }
        private void MainInit()
        {
            NetworkPath = "http://www.odnoklassniki.ru";
            IsContinue = true;
        }

        #endregion

        #region Overrides

        protected override NetworkPerson GetDataFromMember()
        {

            #region Init
            var age = 0;
            var person = new NetworkPerson();
            person.Sites.Add(Sites.Odnoklassniki, Browser.Url);
            var sp = Browser.Spans
                .Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "mctc_infocontainer").
                FirstOrDefault();

            #endregion

            #region Gender

            if (!string.IsNullOrWhiteSpace(_tmp))
            {
                if (_tmp.Contains("\"male\":true"))
                    person.Gender = Gender.Male;
                if (_tmp.Contains("\"male\":false"))
                    person.Gender = Gender.Female;
            }

            #endregion

            #region Age and location
            if (sp != null && sp.Exists)
            {
                var loc = string.Empty;
                GetAgeAndLocation(sp.Text, ref age, ref loc);
                person.Location = loc;
            }
            #endregion

            #region Names

            var n = Browser.Spans
                .Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "mctc_name")
                .Select(item => item.Text).FirstOrDefault();
            person.ShowName = n;
            var nms = n.Replace(" (", "(").Replace(" \\", "\\").Replace(" /", "/").Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (nms.Count() > 0)
                person.Name = nms[0];
            if (nms.Count() > 1)
                person.SurName = nms[1];
            if (person.SurName.Contains("("))
            {
                var surs = person.SurName.Split(new[] { '(', ')' }, StringSplitOptions.RemoveEmptyEntries);
                if (surs.Count() > 0)
                    person.SurName = surs[0];
                if (surs.Count() > 1)
                    person.MaidenName = surs[1];
            }
            else if (person.SurName.Contains("/"))
            {
                var surs = person.SurName.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                if (surs.Count() > 0)
                    person.SurName = surs[0];
                if (surs.Count() > 1)
                    person.MaidenName = surs[1];
            }
            else if (person.SurName.Contains("\\"))
            {
                var surs = person.SurName.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
                if (surs.Count() > 0)
                    person.SurName = surs[0];
                if (surs.Count() > 1)
                    person.MaidenName = surs[1];
            }

            #endregion

            #region Main picture
            var dp = Browser.Div(Find.ById("hook_Block_LeftColumnTopCardFriend"));
            if (dp != null && dp.Exists)
            {
                var dr = dp.Div(Find.ByClass("roundedCard"));
                if (dr != null && dr.Images.Count > 0)
                {
                    person.MainPicture = GetPicture(dr.Images[0]);
                }
            }

            #endregion

            #region Birthday
            var a =
                Browser.Links.Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "mctc_navmenuddlil")
                .Where(item => !string.IsNullOrEmpty(item.Text) && item.Text.ToLower().Contains("праздники"))
                .Where(item => item != null && item.Exists).FirstOrDefault();
            ClickLink(a);

            var dv = Browser.Div(Find.ById("panelHolidaysId"));
            if (dv != null && dv.Exists)
            {
                foreach (var brd in (from span in dv.Spans
                                     where !string.IsNullOrEmpty(span.ClassName)
                                     where span.ClassName.ToLower() == "birthday"
                                     select span.Parent.Parent.Parent).OfType<TableRow>().Select(row => row.Spans.Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "sep-list_add-info_date").FirstOrDefault()).Where(brd => brd != null))
                    person.Dates.Add(Dates.Birthday, GetDate(brd.Text, age));
            }
            #endregion

            Paused();

            dv = Browser.Div(Find.ById("hook_Block_MiddleColumnTopCard_MenuFriend"));
            if (dv != null && dv.Exists)
            {
                var lnk =
                    dv.Links.Where(item => !string.IsNullOrEmpty(item.Text) && item.Text.ToLower().Contains("фото ")).
                        FirstOrDefault();

                ClickLink(lnk);

                var pd = Browser.Div(Find.ById("frPersonalPhotosPanel"));
                if (pd != null)
                    person.Pictures["Основные фотографии"] = new List<DescriptionImage>(GetImages(pd.Table(Find.ByClass("cardsTable"))));

                FillImagesFromAlbums(person.Pictures);

                pd = Browser.Div(Find.ById("frPhotoPinsPanel"));
                if (pd != null)
                    person.Pictures["На фотографиях друзей"] = new List<DescriptionImage>(GetImages(pd.Table(Find.ByClass("cardsTable"))));
            }
            return person;
        }

        private void FillImagesFromAlbums(IDictionary<string, List<DescriptionImage>> pictures)
        {
            var idx = -1;
            while (true)
            {
                idx++;
                var pd = Browser.Div(Find.ById("frPhotoAlbumsPanel"));
                if (pd == null)
                    break;
                var ln3 = pd.Links.FirstOrDefault(itm => itm.Text == "Все фотоальбомы");
                if (ln3 == null)
                    break;
                ClickLink(ln3);
                Paused();
                var cnt = -1;
                var getting = false;
                foreach (var link in Browser.Spans.Where(
                        itm => !string.IsNullOrWhiteSpace(itm.ClassName) && itm.ClassName.ToLower() == "name-label").Select(itm => itm.Links.FirstOrDefault()).Where(itm => itm != null))
                {
                    cnt++;
                    if (cnt != idx) continue;
                    var txt = link.Text;
                    var ims = GetImagesFromAlbum(link);
                    if (ims.Count() <= 0) continue;
                    if (!pictures.ContainsKey(txt))
                        pictures.Add(txt, new List<DescriptionImage>());
                    pictures[txt].AddRange(ims);
                    getting = true;
                }
                if (!getting)
                    break;
            }
        }

        private IEnumerable<DescriptionImage> GetImagesFromAlbum(Element lnk)
        {
            var res = new List<DescriptionImage>();
            if (!ClickLink(lnk))
                return res;
            
            Paused();

            var pd = Browser.Div(Find.ById("photoAlbumPhotos"));
            if (pd != null)
                res.AddRange(GetImages(pd.Table(Find.ByClass("cardsTable"))));

            var dv = Browser.Div(Find.ById("hook_Block_MiddleColumnTopCard_MenuFriend"));
            if (dv != null && dv.Exists)
            {
                var ln =
                    dv.Links.Where(item => !string.IsNullOrEmpty(item.Text) && item.Text.ToLower().Contains("фото ")).
                        FirstOrDefault();
                ClickLink(ln);
            }

            return res;
        }

        private IEnumerable<DescriptionImage> GetImages(IElementContainer tbl)
        {
            var prst = new HashSet<string>();
            var res = new List<DescriptionImage>();
            Div dv = null;
            if (tbl == null)
                return res;
            var lnk =
                (from div in
                        tbl.Divs.Where(
                            item =>
                            !string.IsNullOrEmpty(item.ClassName) &&
                            item.ClassName.ToLower().Contains("centd"))
                    where div.Links.Count > 0
                    select div.Links[0]).FirstOrDefault();
            if (lnk == null)
                return res;
            while (true)
            {
                if (lnk == null)
                    break;
                ClickLink(lnk);
                Paused();
                dv = Browser.Div(Find.ById("popLayerPhotoLayerContainer"));
                lnk =
                    dv.Links.Where(
                        item =>
                        !string.IsNullOrEmpty(item.Text) && item.Text.ToLower().Contains("следующая ")).
                        FirstOrDefault();
                var d = dv.Div(Find.ById("popupPhotoContainerInPopLayer"));
                if (d.Images.Count == 0)
                    continue;
                if (prst.Contains(d.Images[0].Src))
                    break;
                prst.Add(d.Images[0].Src);
                var descr = dv.Div(Find.ByClass("descr"));
                var im = GetPicture(d.Images[0]);
                if (im != null)
                    res.Add(new DescriptionImage {Image = im, Description = descr==null?string.Empty:descr.Text});
            }
            lnk = dv.Links.Where(itm => itm.Id != null && itm.Id.StartsWith("nohook")).FirstOrDefault();
            ClickLink(lnk);
            return res;
        }

        private bool ClickLink(Element lnk)
        {
            if (lnk == null || !lnk.Exists)
                return false;
            try
            {
                lnk.Click();
            }
            catch
            {
            }
            if (IsExistsError())
            {
                MessageBox.Show("You should correct data", "Information");
                if (!lnk.Exists)
                    return false;
                try
                {
                    lnk.Click();
                }
                catch
                {
                }
            }
            return true;
        }

        private bool IsExistsError()
        {
            //var dv = Browser.Div(Find.ById("popLayer_cc"));
            //if (dv == null || !dv.Exists)
                //return false;
            var q =
                Browser.Divs.FirstOrDefault(
                    itm =>
                        !string.IsNullOrWhiteSpace(itm.Text) &&
                    itm.Text.ToLower().Contains("помогите нам в борьбе со спамом!"));
            return (q != null && q.Exists);
                
        }
        protected override bool GoToNextMember()
        {
            _curItem++;
            return GoToMember(_curItem, _curPage);
        }
        private bool GoToMember(int itemIndex, int pageIndex)
        {
            var ln = Browser.Links.FirstOrDefault(itm => !string.IsNullOrWhiteSpace(itm.Text) && itm.Text == "Моя страница");
            ClickLink(ln);
            Paused();
            var dv = Browser.Div(Find.ById("hook_Block_MiddleColumnTopCard_MenuUser"));
            if (dv == null || !dv.Exists)
                return false;
            ln = dv.Links.Where(item => !string.IsNullOrEmpty(item.Text) && item.Text.ToLower().Contains("друзья ")).FirstOrDefault();
            if (!ClickLink(ln))
                return false;

            _curPage = pageIndex;
            _curItem = itemIndex;

            var l =
                Browser.Divs.Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "pager")
                    .Select(
                        htmlElement =>
                        htmlElement.Links.Where(item => item.Text == _curPage.ToString()).FirstOrDefault()).Where(
                            itm => itm != null).FirstOrDefault();
            if (l == null)
            {
                l =
                Browser.Divs.Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "pager")
                    .Select(
                        htmlElement =>
                        htmlElement.Links.Where(item => item.Text == "следующая>").FirstOrDefault()).Where(
                            itm => itm != null).FirstOrDefault();
                ClickLink(l);
                Paused();
                l =
                Browser.Divs.Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "pager")
                    .Select(
                        htmlElement =>
                        htmlElement.Links.Where(item => item.Text == _curPage.ToString()).FirstOrDefault()).Where(
                            itm => itm != null).FirstOrDefault();
            }
            ClickLink(l);
            var frds = Users;
            while (true)
            {
                if (frds.Count() < _curItem)
                {
                    _curItem = 1;
                    _curPage++;
                    var ex = false;
                    foreach (var lnk in
                                    Browser.Divs.Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "pager")
                                    .Select(htmlElement => htmlElement.Links.Where(item => item.Text == _curPage.ToString()).FirstOrDefault()).Where(lnk => lnk != null))
                    {
                        ex = true;
                        ClickLink(lnk);
                        frds = Users;
                    }
                    if (!ex)
                    {
                        var nxt = Browser.Divs.Where(
                            item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower() == "pager")
                            .Select(
                                htmlElement =>
                                htmlElement.Links.Where(item => item.Text == "следующая>").FirstOrDefault()).Where(
                                    lnk => lnk != null).FirstOrDefault();
                        if (nxt == null || !nxt.Exists)
                            return false;
                        ClickLink(nxt);
                        Paused();
                        _curPage--;
                    }
                    continue;
                }
                if (ExistsUser(frds.ElementAt(_curItem - 1).Value))
                {
                    _curItem++;
                    continue;
                }
                _tmp = frds.ElementAt(_curItem - 1).Value;
                ClickLink(frds.ElementAt(_curItem - 1).Key);
                return true;
            }
        }
        protected override void GoToFirstMember(int first = 1)
        {
            GoToMember(first, 1);
        }
        private bool ExistsUser(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
                return false;
            if (Excepts == null || Excepts.Length == 0)
                return false;
            const string key = "\"fio\":\"";
            var idx = name.IndexOf(key);
            if (idx == -1)
                return false;
            var en = name.IndexOf("\",", idx);
            if (en == -1)
                return false;
            var corrected = name.Substring(idx + key.Length, en - idx - key.Length).ToUpper().Trim();
            return Excepts.Where(except => !string.IsNullOrWhiteSpace(except)).Any(except => except.ToUpper().Trim() == corrected);
        }
        protected override void Logout()
        {
            var dv = Browser.Element(Find.ById("mainExit"));
            if (dv == null)
                return;
            var par = dv.Parent;
            if (par == null)
                return;
            ClickLink(par);
        }
        protected override bool  LoginIn()
        {
            try
            {
                Browser.GoTo(NetworkPath);
            }
            catch
            {
            }
            
            var em = Browser.TextField(Find.ById("field_email"));
            if (em == null || !em.Exists)
                return true;
            em.TypeText(Login);
            var ps = Browser.TextField(Find.ById("field_password"));
            if (ps == null || !ps.Exists)
                return true;
                ps.TypeText(Password);
            var bt = Browser.Button(Find.ById("hook_FormButton_button_go"));
            if (bt == null || !bt.Exists)
                return true;
            ClickLink(bt);
            return true;
        }

        #endregion

        #region Methods

        public void Paused()
        {
            var dt = DateTime.Now;
            while ((DateTime.Now-dt).TotalMilliseconds < _pause)
                Application.DoEvents();
        }
        public System.Drawing.Image GetPicture(Image image)
        {
            if (image == null || !image.Exists || string.IsNullOrWhiteSpace(image.Src))
                return null;
            const string t_js =
@"var div = document.images[{0}];
  div.contentEditable ='true';
  var controlRange;
  if(document.body.createControlRange)
  {{
       controlRange = document.body.createControlRange();
       controlRange.addElement(div);
       controlRange.execCommand('Copy');
   }}
   div.contentEditable = 'false';";

            var cnt = -1;
            foreach (var image1 in Browser.Images)
            {
                cnt++;
                if (image1 != null && image1.Exists && !string.IsNullOrWhiteSpace(image1.Src) && image1.Src.ToLower() == image.Src.ToLower())
                    break;
            }
            var script = string.Format(t_js, cnt);
            Browser.RunScript(script);

            var data = Clipboard.GetDataObject();
            if (data == null)
                return null;
            var q = data.GetFormats();
            q.ToString();
            var q2 = data.GetFormats(true);
            q2.ToString();
            if (data.GetDataPresent(DataFormats.Bitmap))
            {
                var img = data.GetData(DataFormats.Bitmap, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.Dib))
            {
                var img = data.GetData(DataFormats.Dib, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.EnhancedMetafile))
            {
                var img = data.GetData(DataFormats.EnhancedMetafile, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.MetafilePict))
            {
                var img = data.GetData(DataFormats.MetafilePict, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.Tiff))
            {
                var img = data.GetData(DataFormats.Tiff, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.Serializable))
            {
                var img = data.GetData(DataFormats.Serializable, true);
                return img as System.Drawing.Image;
            }
            return null;
        }

        public new void Dispose()
        {
            base.Dispose();
            if (_ie != null)
            {
                _ie.Close();
                _ie.Dispose();
                _ie = null;
            }
        }
        private IEnumerable<KeyValuePair<Link,string>> Users
        {
            get
            {
                var tbl = Browser.Table(Find.ByClass("cardsTable"));
                if (tbl == null)
                    return new KeyValuePair<Link, string>[0];
                return (from div in tbl.Divs.Where(item => !string.IsNullOrEmpty(item.ClassName) && item.ClassName.ToLower().Contains("usercard"))
                        select div.Div(Find.ByClass("photoWrapper"))
                        into d where d != null && d.Links.Count != 0 select d.Links[0]
                        into lnk select new KeyValuePair<Link, string>(lnk, lnk.Text)).ToList();

            }
        }
        private static void GetAgeAndLocation(string value, ref int age, ref string location)
        {
            if (string.IsNullOrWhiteSpace(value))
                return;
            var ag = value.Split(new [] {' '})[0].Trim();
            var st = 0;
            if (int.TryParse(ag, out age))
            {
                st = value.IndexOf(",")+1;
                if (st <= 0)
                    st = value.Length-1;
            }
            location = value.Substring(st).Trim();

        }

        private static DateTime GetDate(string birthday, int age)
        {
            var day = 1;
            var month = 1;
            var year = 1900;
            var dt = birthday.Split(new[] { ' ' });
            var num = -1;
            var prev = false;
            var fut = false;
            foreach (var s in dt)
            {
                if (s.ToLower() == "через")
                    fut = true;
                if (s.ToLower() == "назад")
                    prev = true;
                if (num <= 0)
                    int.TryParse(s, out num);
            }

            if (birthday.ToLower().Contains("сегодня"))
            {
                var d = DateTime.Now;
                day = d.Day;
                month = d.Month;
            }
            else if (birthday.ToLower().Trim() == "завтра")
            {
                var d = DateTime.Now.AddDays(1);
                day = d.Day;
                month = d.Month;
            }
            else if (birthday.ToLower().Trim() == "послезавтра")
            {
                var d = DateTime.Now.AddDays(2);
                day = d.Day;
                month = d.Month;
            }
            else if (birthday.ToLower().Trim() == "вчера")
            {
                var d = DateTime.Now.AddDays(-1);
                day = d.Day;
                month = d.Month;
            }
            else if (num > 0 && prev)
            {
                var d = DateTime.Now.AddDays(-num);
                day = d.Day;
                month = d.Month;
            }
            else if (num > 0 && fut)
            {
                var d = DateTime.Now.AddDays(num);
                day = d.Day;
                month = d.Month;
            }
            else
            {
                if (dt.Count() > 1)
                {
                    int.TryParse(dt[0], out day);

                    if (dt[1].ToLower().Contains("январ"))
                        month = 1;
                    else if (dt[1].ToLower().Contains("феврал"))
                        month = 2;
                    else if (dt[1].ToLower().Contains("март"))
                        month = 3;
                    else if (dt[1].ToLower().Contains("апрел"))
                        month = 4;
                    else if (dt[1].ToLower().Contains("ма"))
                        month = 5;
                    else if (dt[1].ToLower().Contains("июн"))
                        month = 6;
                    else if (dt[1].ToLower().Contains("июл"))
                        month = 7;
                    else if (dt[1].ToLower().Contains("август"))
                        month = 8;
                    else if (dt[1].ToLower().Contains("сентябр"))
                        month = 9;
                    else if (dt[1].ToLower().Contains("октябр"))
                        month = 10;
                    else if (dt[1].ToLower().Contains("ноябр"))
                        month = 11;
                    else if (dt[1].ToLower().Contains("декабр"))
                        month = 12;
                }
            }
            if (DateTime.Now.Month < month || (DateTime.Now.Month == month && DateTime.Now.Day < day))
                age++;
            year = DateTime.Now.Year - age;

            return new DateTime(year, month, day);
        }

        #endregion

    }
}
